package com.xcity.game.visit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import com.xcity.db.entity.Player;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.msg.VisitorInPush;
import com.xcity.pomelo.push.msg.VisitorOutPush;
import com.xcity.pomelo.request.VisitRequest;
import com.xcity.pomelo.request.VisitorsRequest;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.pomelo.response.VisitorsResponse;
import com.xcity.util.SessionUtils;

import gnu.trove.list.TLongList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import naga.x.App;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;

@OPHandler
public class VisitService implements Service, EventListener {

	private PlayerCacheService playerCacheService;
	private TLongObjectMap<TLongList> visitors = new TLongObjectHashMap<TLongList>(64);
	
	@Override
	public void startup() throws ServiceException {
		playerCacheService = Objects.requireNonNull(App.getApp().getServiceManager().get(PlayerCacheService.class));
		App.getApp().getEventManager().register(this);
	}

	@Override
	public void shutdown() throws ServiceException {
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] { GameEvents.EVENT_PLAYER_LOGOUT };
	}

	@Override
	public void handleEvent(Event event) {
		playerLogout(event.getParameter());
	}
	
	private void playerLogout(Player player) {
		removeVisitor(player);
	}

	@PomeloOP(PomeloRoute.VISIT)
	protected void requestVisit(Session session, VisitRequest req) {
		if (req.getId() == 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		Player target = playerCacheService.find(req.getId());
		if (target == null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.PLAYER_NOT_EXIST));
			return;
		}
		session.send(new Response(req.getSerial()));
		Player player = session.getClient();
		if (req.getAction() == 0) {
			removeVisitor(target, player);
		} else {
			addVisitor(target, player);
		}
	}
	
	@PomeloOP(PomeloRoute.VISITORS)
	protected void requestVisitors(Session session, VisitorsRequest req) {
		Player player = session.getClient();
		long[] visitorIds = getVisitors(player);
		List<Player> visitors = Collections.emptyList();
		if (visitorIds != null) {
			visitors = new ArrayList<Player>(visitorIds.length);
			for (long id : visitorIds) {
				Player visitor = playerCacheService.find(id);
				if (visitor != null) {
					visitors.add(visitor);
				}
			}
		}
		session.send(new VisitorsResponse(req.getSerial(), visitors));
	}
	
	private synchronized void addVisitor(Player host, Player visitor) {
		TLongList list = visitors.get(host.getId());
		if (list == null) {
			list = new TLongArrayList(5);
			visitors.put(host.getId(), list);
		}
		if (!list.contains(visitor.getId())) {
			list.add(visitor.getId());
			if (list.size() > 5) {
				list.removeAt(0);
			}
		}
		Session sess = visitor.getSession();
		if (sess != null) {
			Long preHostId = sess.setAttribute(SessionUtils.VISIT_HOST_ID, host.getId());
			// 通知上一次参观的目标玩家
			if (preHostId != null) {
				Player preHost = playerCacheService.find(preHostId);
				if (preHost != null && preHost.isOnline()) {
					preHost.send(new VisitorOutPush(visitor));
				}
			}
			// 通知本次参观的目标玩家
			if (host.isOnline()) {
				host.send(new VisitorInPush(visitor));
			}
		}
	}
	
	private synchronized void removeVisitor(Player host, Player visitor) {
		TLongList list = visitors.get(host.getId());
		if (list == null) {
			return;
		}
		list.remove(visitor.getId());
		if (list.isEmpty()) {
			visitors.remove(host.getId());
		}
		Session sess = visitor.getSession();
		if (sess != null) {
			sess.removeAttribute(SessionUtils.VISIT_HOST_ID);
		}
		if (host.isOnline()) {
			host.send(new VisitorOutPush(visitor));
		}
	}
	
	private synchronized void removeVisitor(Player visitor) {
		Session sess = visitor.getSession();
		if (sess != null) {
			Long hostId = sess.getAttribute(SessionUtils.VISIT_HOST_ID);
			if (hostId != null) {
				Player host = playerCacheService.find(hostId);
				if (host != null) {
					removeVisitor(host, visitor);
				}
			}
		}
	}
	
	private long[] getVisitors(Player host) {
		TLongList list = visitors.get(host.getId());
		return list != null ? list.toArray() : null;
	}

}
